#include "TrueText/Config.h"
#include "TextDrawer.h"

#include <cmath>
#include <limits>

#include <TrueText/TextLayout.h>
#include "SpansLayer.h"
#include "FontImpl.h"
#include "BlittingFunction.h"

#include "ft2build.h"
#include FT_FREETYPE_H
#include "FtSpans.h"
#include "TrueText/Error.h"
#include "ftutils.h"
#include "ftcache.h"

#include "DrawerUtils.h"

namespace TrueText {

Rectangle TextDrawer::GetBounds(const TextLayout& layout)
{
	if (cacheLayout == &layout)
		return cacheBounds;

	cacheLayer = DoDraw(layout);
	cacheBounds = GetLayerBounds((SpansLayer*)cacheLayer.get());

	return cacheBounds;
}

boost::shared_ptr<BitmapLayer> TextDrawer::Draw(const TextLayout& layout)
{
	if (cacheLayout == &layout)
		return cacheLayer;

	cacheLayer = DoDraw(layout);
	cacheBounds = GetLayerBounds((SpansLayer*)cacheLayer.get());
	return cacheLayer;
}

boost::shared_ptr<BitmapLayer> TextDrawer::DoDraw(const TextLayout& layout)
{
	FTC_ScalerRec sr = this->font->getScaler();
	std::auto_ptr<SpansLayer> result(new SpansLayer());

	int y_pos = 0;
	std::list<Line>::const_iterator line = layout.Lines.begin();
	std::list<LineMetrics>::const_iterator lineMetrics = layout.Metrics.begin();
	for (; line != layout.Lines.end() && lineMetrics != layout.Metrics.end(); ++line, ++lineMetrics)
	{
		int x_pos = 0;

		std::list<Word>::const_iterator word = line->Words.begin();
		std::list<WordMetrics>::const_iterator wordMetrics = lineMetrics->Words.begin();

		for (; word != line->Words.end() && wordMetrics != lineMetrics->Words.end(); ++word, ++wordMetrics)
		{
			x_pos += wordMetrics->BearingX;
			
			if (!word->IsWhitespace && !word->IsEnterchar)
				for (size_t l = 0; l < word->WordLength; ++l)
				{
					FT_UInt gindex = FTC_CMapCache_Lookup(
						GetCMapCache(),
						reinterpret_cast<FTC_FaceID>(font->getFaceId()),
						-1,
						word->WordStart[l]);
					FT_Glyph glyph;
					FT_EH(FTC_ImageCache_LookupScaler(GetImageCache(), &sr, FT_LOAD_NO_BITMAP, gindex, &glyph, 0));

					if (glyph->format != FT_GLYPH_FORMAT_OUTLINE)
						TRUE_TEXT_THROW(Error(L"Invalid font format - couldn't locate outline for glyph"));

					SpansDefinition def;
					def.spans = &result->GetSpans();
					def.shift_x = x_pos + wordMetrics->LetterPositions[l];
					def.shift_y = y_pos;
					def.height = layout.LineHeight;

					RenderSpans(&((FT_OutlineGlyph)glyph)->outline, &def);
				}

			x_pos += wordMetrics->Bounds.Width;
		}

		y_pos += layout.LineHeight;
	}

	BlittingFunction fn = result->GetBlittingFunction();
	fn.SetColor(font->GetColor());
	fn.SetBlendMode(this->blendMode);
	result->SetBlittingFunction(fn);

	return boost::shared_ptr<BitmapLayer>(result.release());
}

}